﻿// <copyright file="CongressRollCallVotes.cs" company="Microsoft Corporation">
// Copyright (c) 2009 Microsoft Corporation All Rights Reserved
// </copyright>
// <author>Michael S. Scherotter</author>
// <email>mischero@microsoft.com</email>
// <date>2009-4-28</date>
// <summary>Class definition for CongressRollCallVote</summary>

namespace NYTimes
{
    using System;
    using System.ComponentModel;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Linq;
    using System.Text;
    using System.Xml.Linq;
    using NYTimes.Data;    
    
    /// <summary>
    /// the chamber for the vote
    /// </summary>
    public enum Chamber
    {
        /// <summary>
        /// House of Representatives
        /// </summary>
        House,

        /// <summary>
        /// United States Senate
        /// </summary>
        Senate
    }

    /// <summary>
    /// New York Times US Congress Role Call Vote API
    /// </summary>
    /// <remarks>http://developer.nytimes.com/docs/congress_api#h3-votes</remarks>
    public class CongressRollCallVotes : Core.CongressApi
    {
        #region Fields
        /// <summary>
        /// the congress number
        /// </summary>
        private int congressNumber;

        /// <summary>
        /// the session number
        /// </summary>
        private int sessionNumber;

        /// <summary>
        /// the roll call number
        /// </summary>
        private int rollCallNumber;

        /// <summary>
        /// the chamber
        /// </summary>
        private Chamber chamber;

        /// <summary>
        /// the vote info
        /// </summary>
        private Vote vote;

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the congress number
        /// </summary>
        [Description("The Congress Number")]
        public int CongressNumber 
        { 
            get
            {
                return this.congressNumber;
            }

            set
            {
                if (this.congressNumber != value)
                {
                    this.congressNumber = Math.Max(value, 101);

                    this.FetchResults();
                }
            }
        }

        /// <summary>
        /// Gets or sets the chamber
        /// </summary>
        [Description("the Chamber (House or Senate)")]
        public Chamber Chamber 
        {
            get
            {
                return this.chamber;
            }

            set
            {
                if (this.chamber != value)
                {
                    this.chamber = value;

                    this.FetchResults();
                }
            }
        }

        /// <summary>
        /// Gets or sets the session number
        /// </summary>
        [Description("The session number")]
        public int SessionNumber
        {
            get
            {
                return this.sessionNumber;
            }

            set
            {
                if (this.sessionNumber != value)
                {
                    this.sessionNumber = value;

                    this.FetchResults();
                }
            }
        }

        /// <summary>
        /// Gets or sets the roll call number
        /// </summary>
        [Description("The roll call number.")]
        public int RollCallNumber
        {
            get
            {
                return this.rollCallNumber;
            }

            set
            {
                if (this.rollCallNumber != value)
                {
                    this.rollCallNumber = value;

                    this.FetchResults();
                }
            }
        }

        /// <summary>
        /// Gets the vote
        /// </summary>
        public Vote Vote
        {
            get
            {
                return this.vote;
            }

            private set
            {
                if (this.vote != value)
                {
                    this.vote = value;

                    this.TriggerPropertyChanged("Vote");
                }
            }
        }

        #endregion

        #region Methods
        /// <summary>
        /// Clear the vote
        /// </summary>
        public override void ClearResults()
        {
            this.Vote = null;
        }

        /// <summary>
        /// Determines whether the service should be called
        /// </summary>
        /// <returns>true if the congress &gt;= 101, the SessionNumber is 1 or 2, and the RollCallNumber is 1 or greater.</returns>
        public override bool ShouldFetchResults()
        {
            if (this.CongressNumber < 101)
            {
                return false;
            }

            if (this.sessionNumber < 1 || this.sessionNumber > 2)
            {
                return false;
            }

            if (this.RollCallNumber < 1)
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// Parse the XML results
        /// </summary>
        /// <param name="results">the XML results</param>
        public override void OnParseResults(XElement results)
        {
            if (results == null)
            {
                throw new ArgumentNullException("results");
            }

            var voteElement = results.Element("votes").Element("vote");

            this.Vote = new Vote
            {
                Congress = int.Parse(voteElement.Element("congress").Value, CultureInfo.InvariantCulture),
                Session = int.Parse(voteElement.Element("session").Value, CultureInfo.InvariantCulture),
                Chamber = (Chamber)Enum.Parse(typeof(Chamber), voteElement.Element("chamber").Value, true),
                RollCall = int.Parse(voteElement.Element("roll_call").Value, CultureInfo.InvariantCulture),
                Bill = new Bill
                {
                    BillUri = new Uri(voteElement.Element("bill").Element("api_uri").Value, UriKind.Absolute),
                    Title = voteElement.Element("bill").Element("title").Value,
                    LatestMajorAction = voteElement.Element("bill").Element("latest_action").Value,
                },
                Question = voteElement.Element("question").Value,
                Description = voteElement.Element("description").Value,
                VoteType = voteElement.Element("vote_type").Value,
                DateTime = DateTime.Parse(voteElement.Element("date").Value + "T" + voteElement.Element("time").Value, CultureInfo.InvariantCulture),
                Result = voteElement.Element("result").Value,
                Democratic = CreateVoteTotal(voteElement.Element("democratic")),
                Republican = CreateVoteTotal(voteElement.Element("republican")),
                Independent = CreateVoteTotal(voteElement.Element("independent")),
                Total = CreateVoteTotal(voteElement.Element("total")),
                Positions = from position in voteElement.Element("positions").Elements("position")
                            select new VotePosition()
                            {
                                MemberId = position.Element("member_id").Value,
                                Position = position.Element("vote_position").Value
                            },
                VacantSeats = from vacantSeat in voteElement.Element("vacant_seats").Elements("vacant_seat")
                              select new VacantSeat()
                              {
                                  State = vacantSeat.Element("state").Value,
                                  District = int.Parse(vacantSeat.Element("district").Value, CultureInfo.InvariantCulture)
                              }
            };
        }

        /// <summary>
        /// Create the URL
        /// </summary>
        /// <param name="baseUri">the base URI</param>
        /// <returns>the URL for the web service</returns>
        [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "This API works only with lowercase.")]
        public override Uri GenerateUrl(string baseUri)
        {
            var builder = new StringBuilder(baseUri);

            builder.AppendFormat(
                CultureInfo.InvariantCulture,
                "/{0}/{1}/sessions/{2}/votes/{3}.xml?api-key={4}",
                this.CongressNumber,
                this.Chamber.ToString().ToLower(CultureInfo.InvariantCulture),
                this.SessionNumber,
                this.RollCallNumber,
                this.ApiKey);

            return new Uri(builder.ToString(), UriKind.Absolute);
        }
        #endregion
        #region Implementation
        /// <summary>
        /// Create a vote total
        /// </summary>
        /// <param name="vote">the vote element</param>
        /// <returns>the vote total</returns>
        private static VoteTotal CreateVoteTotal(XElement vote)
        {
            var total = new VoteTotal
            {
                MajorityPosition = vote.Element("majority_position") == null ? false : vote.Element("majority_position").Value == "Yes",
                Margin = vote.Element("margin") == null ? null : new int?(int.Parse(vote.Element("margin").Value, CultureInfo.InvariantCulture)),
                No = int.Parse(vote.Element("no").Value, CultureInfo.InvariantCulture),
                NotVoting = int.Parse(vote.Element("not_voting").Value, CultureInfo.InvariantCulture),
                Present = int.Parse(vote.Element("present").Value, CultureInfo.InvariantCulture),
                Yes = int.Parse(vote.Element("yes").Value, CultureInfo.InvariantCulture)
            };

            return total;
        }

        #endregion
    }
}
